﻿using System;
using System.Collections.Generic;
using Bouyei.Geo.Converters;
using Bouyei.Geo.Geometries;


namespace Bouyei.Geo.GeoParser
{
    public class EsriMdbParser:BaseBytesParser
    {
        private BitExtensions bitConvert = null;

        public GeoType geometryType { get; set; }

        public Coordinate Min { get; set; }

        public Coordinate Max { get; set; }

        public EsriMdbParser(byte[] array)
            :base(array)
        {
            this.array = array;
            bitConvert = new BitExtensions(true);
        }

        public unsafe Geometry FromReader()
        {
            fixed (byte* ptr = &array[0])
            {
                int type = bitConvert.ToInt32(ptr);
                if (type == 1) geometryType = GeoType.POINT;
                else if (type == 3) geometryType = GeoType.LINESTRING;
                else if (type == 5) geometryType = GeoType.POLYGON;
                else if (type == 8) geometryType = GeoType.MULTIPOINT;
                else throw new Exception("geometryType not supported:" + type);

                if (geometryType == GeoType.POINT)
                {
                    var coord = new Coordinate()
                    {
                        X = bitConvert.ToDouble(ptr + 4),
                        Y = bitConvert.ToDouble(ptr + 12)
                    };
                    return new Geometry(coord);
                }

                Min = new Coordinate()
                {
                    X = bitConvert.ToDouble(ptr + 4),
                    Y = bitConvert.ToDouble(ptr + 12)
                };
                Max = new Coordinate()
                {
                    X = bitConvert.ToDouble(ptr + 20),
                    Y = bitConvert.ToDouble(ptr + 28)
                };
                if (geometryType == GeoType.MULTIPOINT)
                {
                    var coordinates = ParserToMultiPoint(ptr + 36);
                    return new Geometry(GeoType.MULTIPOINT, coordinates);
                }
                else
                {
                    var coordinates = ParserToArray(ptr + 36);
                    return new Geometry(geometryType, coordinates);
                }
            }
        }

        private unsafe List<Coordinate[]> ParserToArray(byte* ptr)
        {
            int count = bitConvert.ToInt32(ptr);
            List<Coordinate[]> coordinates = new List<Coordinate[]>(count);

            for (int j = 0; j < count; ++j)
            {
                long vertex = bitConvert.ToInt64(ptr + 4);
                Coordinate[] coords = new Coordinate[vertex];

                int start = 12;
                for (int i = 0; i < coords.Length; ++i)
                {
                    var lon = bitConvert.ToDouble(ptr + start);
                    var lat = bitConvert.ToDouble(ptr + start + 8);
                    coords[i] = new Coordinate()
                    {
                        X = lon,
                        Y = lat
                    };

                    start += 16;
                }
                coordinates.Add(coords);
            }

            return coordinates;
        }

        private unsafe Coordinate[] ParserToMultiPoint(byte* ptr)
        {
            int count = bitConvert.ToInt32(ptr);
            Coordinate[] coords = new Coordinate[count];
            int start = 4;

            for (int i = 0; i < count; ++i)
            {
                var lon = bitConvert.ToDouble(ptr + start);
                var lat = bitConvert.ToDouble(ptr + start + 8);
                coords[i] = new Coordinate()
                {
                    X = lon,
                    Y = lat
                };

                start += 16;
            }
            return coords;
        }
    }
}
